package DataIO;

public class SolutionRecuit{

	private int matriceDecisions[][][];
	private double matriceProduction[][];
	private int nbJours;
	private int nbCentrales;
	private int nbPaliersCentrales[];
	private double fonctionObj;
	private int tauxAcceptation;
	
	public SolutionRecuit(){
		this.nbJours = 7;
		this.nbCentrales = 5;
		this.tauxAcceptation = 95;
		this.fonctionObj = Double.MIN_VALUE;
		this.nbPaliersCentrales = new int[this.nbCentrales];
		this.nbPaliersCentrales[0] = 3;
		this.nbPaliersCentrales[1] = 4;
		this.nbPaliersCentrales[2] = 4;
		this.nbPaliersCentrales[3] = 5;
		this.nbPaliersCentrales[4] = 8;
		
		this.matriceDecisions = new int[this.nbCentrales][][];
		for(int i = 0 ; i < this.nbCentrales ; ++i)
				this.matriceDecisions[i] = new int[this.nbJours][this.nbPaliersCentrales[i]];
		
		this.matriceProduction = new double[this.nbCentrales][this.nbJours];
	}
	
	/**
	 * Cree une solution aleatoire initiale en vérifiant si elle est realisable ou non
	 * @param d Les données
	 * @param bt Les données binaires
	 * @param bh Les données binaires
	 */
	public void genererSolutionInitial(Data d, BinaireThermique bt, BinaireHydraulique bh){
		int random;
		do{
			for (int i = 0 ; i < this.nbCentrales ; ++i){
				for (int j = 0 ; j < this.nbJours ; ++j){
					random = (int)(Math.random()*this.nbPaliersCentrales[i]);
					for (int k = 0 ; k < this.nbPaliersCentrales[i] ; ++k){
						if(k == random)
							this.matriceDecisions[i][j][k] = 1;
						else
							this.matriceDecisions[i][j][k] = 0;
					}
				}
			}
			mettreAjourFonctionObj(d, bt, bh);
		}while(fonctionObj < 0.1);
	}
	
	/**
	 * Vérifie qu'une solution est réalisable ou non et si oui, met à jour la fonction objective de cette solution
	 * @param d Les données
	 * @param bt Les données binaires
	 * @param bh Les données binaires
	 */
	private void mettreAjourFonctionObj(Data d, BinaireThermique bt, BinaireHydraulique bh) {
		double donneesTherm[][][][] = bt.getScenarios();
		double donneesPaliers[][] = bt.getPaliers();
		double donneesHydrau[][] = bh.getTrajectoires();
		double donneesDemande[][] = d.getDemande();
		double donneesCout[] = d.getCout();
		double donneesRho = bh.getRho();
		double test = 0.0d;
		double val;
		boolean flag;
		int tabResultat[] = new int[donneesTherm[0][0][0].length];
		fonctionObj = 0.0d;
		
		for (int l = 0 ; l < donneesTherm[0][0][0].length ; ++l){
			flag = true;
			for (int j = 0 ; j < this.nbJours ; ++j){
				test = 0.0d;
				val = 0.0d;
				for (int i = 0 ; i < this.nbCentrales ; ++i){
					for (int k = 0 ; k < (this.nbPaliersCentrales[i]) ; ++k){
						if (matriceDecisions[i][j][k] == 1){
							if (i == this.nbCentrales - 1)
								test += donneesHydrau[j][k];
							else
								test += donneesTherm[i][j][k][l];
						}
					}
				}
				if (test < donneesDemande[j][l])
					flag = false;
			}
			if (flag)
				tabResultat[l] = 1;
			else
				tabResultat[l] = 0;
		}
		int cpt = 0;
		for (int l = 0 ; l < donneesTherm[0][0][0].length ; ++l){
			if (tabResultat[l] == 1) cpt++;
		}
		
		if (cpt >= this.tauxAcceptation){
			for (int j = 0 ; j < this.nbJours ; ++j){
				for (int i = 0 ; i < this.nbCentrales ; ++i){
					for (int k = 0 ; k < (this.nbPaliersCentrales[i]) ; ++k){
						if (matriceDecisions[i][j][k] == 1){
							if (i == this.nbCentrales - 1){
								matriceProduction[i][j] = donneesHydrau[j][k]*donneesRho;
								fonctionObj += donneesHydrau[j][k]*donneesCout[i]*donneesRho;
							}
							else{
								matriceProduction[i][j] = donneesPaliers[i][this.nbPaliersCentrales[i] - 1];
								fonctionObj += donneesPaliers[i][this.nbPaliersCentrales[i] - 1]*donneesCout[i];
							}
						}
					}
				}
			}
		}
		else
			fonctionObj = Float.MIN_VALUE;
	}

	/**
	 * Génére une solution voisine à partir de la solution initiale. Pour cela, on selectionne une centrale au hasard et on modifie ses paliers pour chacun des jours
	 * @param d Les données
 	 * @param bt Les données binaires
	 * @param bh Les données binaires
	 * @return La solution voisine
	 */
	public SolutionRecuit genererSolutionVoisine(Data d, BinaireThermique bt, BinaireHydraulique bh) {
		SolutionRecuit sol = null;
		do{
			sol = this;
			int random = (int)(Math.random()*sol.nbCentrales);
			for (int i = 0 ; i < sol.nbJours ; ++i){
				int random2;
				do{
					random2 = (int)(Math.random()*sol.nbPaliersCentrales[random]);
				}while(sol.matriceDecisions[random][i][random2] == 1);
				for (int j = 0 ; j < sol.nbPaliersCentrales[random] ; ++j){
					if (j == random2)
						sol.matriceDecisions[random][i][j] = 1;
					else
						sol.matriceDecisions[random][i][j] = 0;
				}
			}
			sol.mettreAjourFonctionObj(d, bt, bh);
		}while(sol.fonctionObj < 0.1);
		return sol;
	}

	/**
	 * Affiche les données de la solution
	 */
	public String toString(){
		String res = "obj : " + fonctionObj + "\n";
		for(int i = 0 ; i < this.nbCentrales ; ++i){
			res+="centrale " + (i+1) + "\n";
			for (int j = 0 ; j < this.nbJours ; ++j){
				for (int k = 0 ; k < this.nbPaliersCentrales[i] ; ++k){
					res += this.matriceDecisions[i][j][k];
				}
				res+="\n";
			}
		}
		return res;
	}
	
	public double getFonctionObj() {
		return this.fonctionObj;
	}
	public double[][] getProduction(){
		return this.matriceProduction;
	}
	
}